package com.enjoyor.soa.traffic.frame.support.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.enjoyor.soa.traffic.frame.security.bean.dto.UserDto;
import com.enjoyor.soa.traffic.frame.support.bean.ParamObject;
import com.enjoyor.soa.traffic.frame.support.bean.ExcelErrorPojo;
import com.enjoyor.soa.traffic.util.enums.EnumAppCode;
import com.enjoyor.soa.traffic.util.exception.NoServiceException;
import com.enjoyor.soa.traffic.util.helper.*;
import com.enjoyor.soa.traffic.util.helper.export.CustomHSSFCell;
import com.enjoyor.soa.traffic.util.helper.export.ExcelExportTools;
import com.enjoyor.soa.traffic.util.pojo.ExportCell;
import com.enjoyor.soa.traffic.util.pojo.ExportCells;
import com.enjoyor.soa.traffic.util.pojo.Page;
import com.enjoyor.soa.traffic.util.pojo.ResultPojo;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public abstract class BaseController {
	private static final Logger log=LoggerFactory.getLogger(BaseController.class.getSimpleName());

    @SuppressWarnings("rawtypes")
    protected String doRequest(HttpServletRequest request, HttpServletResponse response, Object objOwner,
            String methodName, Class bean, String[] fieldNames){
        this.setAccessHeader(response);
        ParamObject paramObject = this.getParamObject(request, bean);
        Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
        ResultPojo resultPojo = this.methodInvoke(objOwner, methodName, args);
        return this.getResultString(resultPojo, paramObject.getCallback());
    }

    @SuppressWarnings("rawtypes")
    public String addByExcel(HttpServletRequest request, HttpServletResponse response,Object objOwner,String methodName,Class bean) {
        this.setAccessHeader(response);
        String token=getToken(request);
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(1024 * 1024);
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setHeaderEncoding("utf-8");
        try {
            List<Workbook> listWorkBook = new ArrayList<>();
            List<FileItem> list = upload.parseRequest(request);
            for (FileItem item : list) {
                Workbook workbook;
                workbook = getWorkbook(item);
                if (workbook != null) {
                    listWorkBook.add(workbook);
                }
            }
            ResultPojo result = this.insertByExcel(listWorkBook,bean,objOwner,methodName,token);
            String domain = "location.hostname"; //设置domain，用于跨域
            String setDomain = "<script>document.domain = "+domain+";</script>";
            return setDomain+JSON.toJSONString(result.convertToJson(),SerializerFeature.WriteMapNullValue,SerializerFeature.WriteNullStringAsEmpty);
        } catch (Exception e) {
            LoggerHelper.LOG.error(e);
            return ResultHelper.getResult(e).convertToJson().toJSONString();
        }
    }

    @SuppressWarnings("rawtypes")
    protected ResultPojo doRequestResult(HttpServletRequest request, HttpServletResponse response, Object objOwner,
            String methodName, Class bean, String[] fieldNames){
        this.setAccessHeader(response);
        ParamObject paramObject = this.getParamObject(request, bean);
        Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
        ResultPojo resultPojo = this.methodInvoke(objOwner, methodName, args);
        return resultPojo;
    }

    @SuppressWarnings("rawtypes")
    protected String doEasyUi(HttpServletRequest request, HttpServletResponse response, Object objOwner,
            String methodName, Class bean, String[] fieldNames){
        this.setAccessHeader(response);
        ParamObject paramObject = this.getParamObject(request, bean);
        Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
        ResultPojo resultPojo = this.methodInvoke(objOwner, methodName, args);
        Page page = (Page) resultPojo.getResultList();
        resultPojo = ResultHelper.getResult(page.ConvertToPageEasyUI());
        return this.getResultString(resultPojo, paramObject.getCallback());
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected String doBootUiRequest(HttpServletRequest request, HttpServletResponse response, Object objOwner,
            String methodName, Class bean, String[] fieldNames){
        Map<String, Object> map = new HashMap();
        String drawParam = request.getParameter("draw");
        if (drawParam != null && !drawParam.equals("")) {
        	try {
        		map.put("draw", Integer.parseInt(request.getParameter("draw")));
        	}catch(NumberFormatException e) {
            	String errorContext=this.getClass().getSimpleName()+"����doBootUiRequest����ʱ����תint����!";
            	LoggerHelper.writeErrorLogInfo(errorContext, objOwner.getClass().getSimpleName(), methodName, null,e);
        	}
        } else {
            map.put("draw", 0);
        }

        this.setAccessHeader(response);
        ParamObject paramObject = this.getParamObject(request, bean);
        Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
        ResultPojo resultPojo = this.methodInvoke(objOwner, methodName, args);
        if (resultPojo.getAppCode().equals("0")) {
            Page page = (Page) resultPojo.getResultList();
            map.put("recordsTotal", page.getTotalCount());
            map.put("recordsFiltered", page.getTotalCount());
            map.put("resultPojo", resultPojo);
        } else {
            map.put("recordsTotal", paramObject.getPage().getTotalCount());
            map.put("recordsFiltered", paramObject.getPage().getTotalCount());
            map.put("resultPojo", "");
        }

        return this.getResultStringMap(map, paramObject.getCallback());
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected String doExport(HttpServletRequest request, HttpServletResponse response, Object objOwner,
            String methodName, Class bean, String[] fieldNames, String filename, CustomHSSFCell[] headers){
        this.setAccessHeader(response);
        ResultPojo result = new ResultPojo();
        ParamObject paramObject = this.getParamObject(request, bean);

        try {
            Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
            ResultPojo resultPojo = this.methodInvoke(objOwner, methodName, args);
            if (resultPojo.getAppCode().equals("0")) {
                List<T> rp = (List) resultPojo.getResultList();
                ExcelExportTools et = new ExcelExportTools();
                Map<String, Object> map = this.getExportInfo(request, filename);
                boolean flag = et.exportExcelOfFieldName(rp, headers, (String) map.get("filePath"), filename,
                        "yyyy-MM-dd hh:mm:ss");
                if (flag) {
                    result = ResultHelper.getResult(map.get("fileRelativePath"));
                } else {
                    result = ResultHelper.getResult(EnumAppCode.EXPORT_FAILURE);
                }
            }
        } catch (Exception e) {
        	String errorContext=filename+"����ʧ�ܣ�";
        	Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
        	LoggerHelper.writeErrorLogInfo(errorContext, this.getClass().getSimpleName(), methodName, e ,args);
            result = ResultHelper.getResult(e);
        }

        return this.getResultString(result, paramObject.getCallback());
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Map<String, Object> getExportInfo(HttpServletRequest request, String fileHead) {
        Map<String, Object> map = new HashMap();
        String rootPath = request.getSession().getServletContext().getRealPath("/ExcelExportTempFolder");
        String fileName = fileHead + TimeHelper.dateToString(new Date(), "yyyyMMddHHmmssSS");
        String filePath = rootPath + File.separator + fileName + ".xls";
        map.put("filePath", filePath);
        String fileRelativePath = "ExcelExportTempFolder/" + fileName + ".xls";
        map.put("fileRelativePath", fileRelativePath);
        return map;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected String doExport(HttpServletRequest request, HttpServletResponse response, Object objOwner,
            String methodName, Class bean, String[] fieldNames, String xmlname) {
        this.setAccessHeader(response);
        ResultPojo result = new ResultPojo();
        ParamObject paramObject = this.getParamObject(request, bean);
        try {
            Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
            ResultPojo resultPojo = this.methodInvoke(objOwner, methodName, args);
            if (resultPojo.getAppCode().equals("0")) {
            	List<T> rp=null;
                if(resultPojo.getResultList() instanceof Page) {
            		Page page=(Page)resultPojo.getResultList();
            		 rp = (List) page.getResult();
            	}
            	else
            	{
                     rp = (List) resultPojo.getResultList();
            	}
				if (rp.size() > 65535) { // �����Ҫ��������������65535����ֻ����65535
//                    result = ResultHelper.getResult(EnumAppCode.EXPORT_TOOMUCH);
//                    return this.getResultString(result, paramObject.getCallback());
					  List<T> temp=new LinkedList<>();
					  for(int i=0;i<65535;i++) {
						  temp.add(i, rp.get(i));
					  }
					  rp=temp;
                }

                if (rp.size() == 0) {
                    result = ResultHelper.getResult(EnumAppCode.EXPORT_NULL);
                    return this.getResultString(result, paramObject.getCallback());
                }
                ExcelExportTools<T> et = new ExcelExportTools();
                Map<String, Object> map = this.getCellsByPath(request, xmlname);
                boolean flag = et.exportExcelOfFieldName(rp, (CustomHSSFCell[]) map.get("header"),
                        (String) map.get("filePath"), (String) map.get("filename"), "yyyy-MM-dd hh:mm:ss");
                if (flag) {
                	UserDto userDto = (UserDto) request.getAttribute("currentUser");
                	String userName=userDto.getSsoUserName();
                	LoggerHelper.LOG.info(userName+"������"+xmlname+"��Excel�ļ�");
                    result = ResultHelper.getResult(map.get("fileRelativePath"));
                } else {
                    result = ResultHelper.getResult(EnumAppCode.EXPORT_FAILURE);
                }
            }
        } catch (Exception e) {
        	String errorContext=xmlname+"����ʧ�ܣ�";
        	Object[] args = this.getParamsArgs(paramObject, fieldNames, bean);
        	LoggerHelper.writeErrorLogInfo(errorContext, this.getClass().getSimpleName(), methodName, e ,args);
            result = ResultHelper.getResult(e);
        }

        return this.getResultString(result, paramObject.getCallback());
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Map<String, Object> getCellsByPath(HttpServletRequest request, String names) {
        HashMap map = new HashMap();

        try {
            Resource resource = new ClassPathResource("static/exportXml/" + names + ".xml");
            InputStream inputStream = resource.getInputStream();
            ExportCells cells = (ExportCells) JaxbHelper.unmarshal(ExportCells.class,inputStream);
            List<ExportCell> list = cells.getExportCells();
            CustomHSSFCell[] header = new CustomHSSFCell[list.size()];

            for (int a = 0; a < list.size(); ++a) {
                ExportCell cell = (ExportCell) list.get(a);
                header[a] = new CustomHSSFCell(cell.getName(), cell.getColumn(), cell.getRow(), cell.getColwidth(),
                        cell.getFieldname());
            }

            map.put("header", header);
            map.put("filename", cells.getFilename());
            File file = new File(request.getSession().getServletContext().getRealPath("/") + "ExcelExportTempFolder");
            if (!file.exists() && !file.isDirectory()) {
                file.mkdirs();
            }
            String rootPath = request.getSession().getServletContext().getRealPath("/ExcelExportTempFolder");
            String fileName = cells.getFilename() + TimeHelper.dateToString(new Date(), "yyyyMMddHHmmssSS");
            String filePath = rootPath + File.separator + fileName + ".xls";
            map.put("filePath", filePath);
            String fileRelativePath = "ExcelExportTempFolder/" + fileName + ".xls";
            map.put("fileRelativePath", fileRelativePath);
            return map;
        } catch (Exception e) {
            return new HashMap();
        }
    }

    @SuppressWarnings("rawtypes")
    private ParamObject getParamObject(HttpServletRequest request, Class bean) {
        ParamObject paramObject = new ParamObject();
        try {
            paramObject.setData(request.getParameter("data"));
            paramObject.setDataMap(JsonHelper.jsonStrToMap(paramObject.getData()));
            paramObject.setCallback(request.getParameter("callback"));
            paramObject.setToken(request.getHeader("token"), request);
            paramObject.setSystemKey((String) request.getAttribute("systemKey"), request);
            paramObject.setObject(bean);
            paramObject.setPage();
            paramObject.setOrderSeq();
        } catch (Exception e) {
        	String errorContext=this.getClass().getSimpleName()+"����getParamObject����ʱ��request��ȡ��������!";
        	Map<String,Object> map=paramObject.getDataMap();
        	LoggerHelper.writeErrorLogInfo(errorContext, this.getClass().getSimpleName(),"getParamsArgs",map,e);
        }

        return paramObject;
    }

    private void setAccessHeader(HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", "*");
    }

    @SuppressWarnings("rawtypes")
    private Object[] getParamsArgs(ParamObject paramObject, String[] fieldNames, Class bean) {
        Object[] args = null;

        try {
            if (fieldNames != null && fieldNames.length > 0) {
                args = new Object[fieldNames.length];

                for (int i = 0; i < fieldNames.length; ++i) {
                    if (!fieldNames[i].equals("token") && !fieldNames[i].equals("page")
                            && !fieldNames[i].equals("orderSeq") && !fieldNames[i].equals("dto")
                            && !fieldNames[i].equals("systemKey") && !fieldNames[i].equals("sessionUser")) {
                        Map<String, Object> map = paramObject.getDataMap();
                        args[i] = map.get(fieldNames[i]);
                    } else {
                        Field field = ParamObject.class.getDeclaredField(fieldNames[i]);
                        field.setAccessible(true);
                        args[i] = field.get(paramObject);
                    }
                }
            }
        } catch (Exception e) {
        	String errorContext=this.getClass().getSimpleName()+"����getParamsArgs����ʱ�������!";
        	Map<String,Object> map=paramObject.getDataMap();
        	LoggerHelper.writeErrorLogInfo(errorContext, this.getClass().getSimpleName(),"getParamsArgs",map,e);
        }

        return args;
    }

    @SuppressWarnings("rawtypes")
    private ResultPojo methodInvoke(Object objOwner, String methodName, Object[] args) {
        try {
            Class[] argsClass = null;
            if (args != null && args.length > 0) {
                argsClass = new Class[args.length];

                for (int i = 0; i < args.length; ++i) {
                    if (args[i] == null) {
                        argsClass[i] = String.class;
                    } else {
                        argsClass[i] = args[i].getClass();
                    }
                }
            }

            Method method = objOwner.getClass().getMethod(methodName, argsClass);
            ResultPojo result=(ResultPojo) method.invoke(objOwner, args);
            return result;
        } catch (SecurityException | NoSuchMethodException e) {
        	String errorContext="����"+methodName+"����!";
        	LoggerHelper.writeErrorLogInfo(errorContext, objOwner.getClass().getSimpleName(), methodName, e,args);
            return ResultHelper.getResult(e,EnumAppCode.NO_SUCH_METHOD);
        } catch (IllegalAccessException e) {
        	String errorContext="����"+methodName+"����!";
        	LoggerHelper.writeErrorLogInfo(errorContext, objOwner.getClass().getSimpleName(), methodName, e,args);
            return ResultHelper.getResult(e,EnumAppCode.ILLEGAL_ACCESS);
        } catch (IllegalArgumentException e) {
        	String errorContext="����"+methodName+"����!";
        	LoggerHelper.writeErrorLogInfo(errorContext, objOwner.getClass().getSimpleName(), methodName, e,args);
            return ResultHelper.getResult(e,EnumAppCode.ILLEGAL_ARGUMEN);
        } catch (InvocationTargetException e) {
        	Throwable t=e.getTargetException();
        	if(t instanceof NoServiceException) {
        		//���쳣��manage�㲶����¼��־,������ֻ��Ҫ����ResultPojo
            	return ResultHelper.getResult(e);
        	}
        	else {
        		String errorContext="����"+methodName+"����!";
            	LoggerHelper.writeErrorLogInfo(errorContext, objOwner.getClass().getSimpleName(), methodName, e,args);
                return ResultHelper.getResult(e);
        	}
        }
        catch (Exception e) {
        	String errorContext="����"+methodName+"����!";
        	LoggerHelper.writeErrorLogInfo(errorContext, objOwner.getClass().getSimpleName(), methodName, e,args);
            return ResultHelper.getResult(e);
        }
    }

    private String getResultString(ResultPojo resultPojo, String callback) {
        String strResult = null;
        if (callback != null && !callback.equals("")) {
            if (resultPojo != null) {
                strResult = callback + "(" + resultPojo.convertToJson().toJSONString() + ")";
            } else {
                strResult = callback + "()";
            }
        } else if (resultPojo != null) {
            strResult = JSON.toJSONString(resultPojo.convertToJson(),SerializerFeature.WriteMapNullValue,SerializerFeature.WriteNullStringAsEmpty);
        }

        return strResult;
    }

    @SuppressWarnings("rawtypes")
    private String getResultStringMap(Map map, String callback) {
        String strResult = null;
        if (callback != null && !callback.equals("")) {
            if (map != null) {
                strResult = callback + "(" + JSON.toJSONString(map) + ")";
            } else {
                strResult = callback + "()";
            }
        } else if (map != null) {
            strResult = JSON.toJSONString(map);
        }

        return strResult;
    }


    private Workbook getWorkbook(FileItem item) {
        Workbook workbook = null;
        String extName = ExcelToListHelp.getPostfix(item.getName());
        if (extName.equals("xls")) {// 2003版本的excel，用.xls结尾
            try {
                workbook = new HSSFWorkbook(item.getInputStream());// 得到工作簿
            } catch (IOException ex) {
                LoggerHelper.LOG.error(ex);
            }
        } else if (extName.equals("xlsx")) {// 2007版本的excel，用.xlsx结尾
            try {
                workbook = new XSSFWorkbook(item.getInputStream());// 得到工作簿
            } catch (IOException ex) {
                LoggerHelper.LOG.error(ex);
            }
        }
        return workbook;
    }


    private ResultPojo insertByExcel(List<Workbook> list,Class bean,Object objOwner,String methodName,String token) {
        for (Workbook workbook : list) {
                int successNum=0;
                int failNume=0;
                Sheet sheet = workbook.getSheetAt(0);
                List<String> keyList=new ArrayList<>(); //获取字段名称
                Row keyRow=sheet.getRow(sheet.getFirstRowNum()+1); //excel的模板第二行为插入信息的字段名称
                ResultPojo insertResult=new ResultPojo(); //整合结果后返回，resultlist里面列出具体出错行和出错原因
                List<ExcelErrorPojo> errorreason=new ArrayList<>(); //错误原因

                for(int cellnum=keyRow.getFirstCellNum();cellnum<keyRow.getLastCellNum();cellnum++){
                    Cell cell=keyRow.getCell(cellnum);
                    keyList.add(cell.getStringCellValue());
                }

                for (int rownum = sheet.getFirstRowNum()+3; rownum < sheet.getPhysicalNumberOfRows(); rownum++) {
                    try {
                        Row row = sheet.getRow(rownum);
                        if (row != null) {
                            JSONObject jsonObj = new JSONObject();
                            for (int cellnum = row.getFirstCellNum(); cellnum < row.getLastCellNum(); cellnum++) {
                                Cell cell = row.getCell(cellnum);
                                cell.setCellType(Cell.CELL_TYPE_STRING);
                                jsonObj.put(keyList.get(cellnum), cell.getStringCellValue());
                            }
                            Object dto = JsonHelper.jsonToObject(jsonObj.toJSONString(), bean);
                            Object[] args = new Object[2];
                            args[0] = token;
                            args[1] = dto;
                            ResultPojo result = this.methodInvoke(objOwner, methodName, args);
                            if(result.getAppCode().equals("0")){
                                successNum++;
                            }
                            else{
                                failNume++;
                                ExcelErrorPojo error=new ExcelErrorPojo();
                                error.setErrorLineNumber(rownum+1);
                                error.setErrorReason(result.getDataBuffer());
                                errorreason.add(error);
                            }
                        }
                    }catch(Exception e){
                        failNume++;
                        ExcelErrorPojo error=new ExcelErrorPojo();
                        error.setErrorLineNumber(rownum+1);
                        error.setErrorReason(e.getMessage());
                        errorreason.add(error);
                    }
                }
            String dataBuffer="共插入["+(successNum+failNume)+"]条数据。成功["+successNum+"]条，失败["+failNume+"]条。\r\n详细失败信息见INFO日志";
            insertResult.setDataBuffer(dataBuffer);
            insertResult.setResultList(errorreason);
            LoggerHelper.LOG.info(methodName+dataBuffer+"详细错误原因："+ ListHelper.listToStr(errorreason));
            return insertResult;
        }
        return null;
    }


    private String getToken(HttpServletRequest request){
        String sessionToken = (String) request.getSession().getAttribute("const_token");
        String token = request.getHeader("token") == null ? request.getParameter("token") : request.getHeader("token");
        if (token != null && !token.equals("")) {
            return token;
        } else if (sessionToken != null && !sessionToken.equals("")) {
            token = sessionToken;
            return token;
        } else {
            Cookie[] cookies = request.getCookies();
            Cookie[] var8 = cookies;
            int var7 = cookies.length;

            for (int var6 = 0; var6 < var7; ++var6) {
                Cookie cookie = var8[var6];
                if ("token".equals(cookie.getName())) {
                    token = cookie.getValue();
                    return token;
                }
            }
            return null;
        }
    }



}
